* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+#include <config_features.h>
+
#include <test/unoapixml_test.hxx>
#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
#include <extendedprimitive2dxmldump.hxx>
#include <rtl/ustring.hxx>
+#include <sfx2/objsh.hxx>
#include <svx/sdr/contact/displayinfo.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/svdomedia.hxx>
#include <svx/svdpage.hxx>
#include <svx/unopage.hxx>
#include <vcl/virdev.hxx>
// i.e. the rendering did not find the bitmap.
assertXPath(pDocument, "//bitmap", 1);
}
+
+CPPUNIT_TEST_FIXTURE(SdrTest, testMediaLinkNotFetchedWhenUpdatesDisallowed)
+{
+ // A media object referenced by an external xlink:href, with no copy of
+ // the media stored inside the document.
+ loadFromFile(u"media-link.fodp");
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+ uno::UNO_QUERY);
+ auto* pSvxDrawPage = dynamic_cast<SvxDrawPage*>(xDrawPage.get());
+ CPPUNIT_ASSERT(pSvxDrawPage);
+ auto* pMedia = dynamic_cast<SdrMediaObj*>(pSvxDrawPage->GetSdrPage()->GetObj(0));
+ CPPUNIT_ASSERT(pMedia);
+
+ // With link updates disallowed, as when loading or converting a document
+ // without a user present to approve them, asking for the placeholder image
+ // must not reach out to the external URL. Without the fix the snapshot is
+ // generated (an empty placeholder bitmap even where no media backend is built,
+ // and the referenced content where one is), so the returned graphic is set.
+ SfxObjectShell* pShell = SfxObjectShell::GetShellFromComponent(mxComponent);
+ CPPUNIT_ASSERT(pShell);
+ pShell->getEmbeddedObjectContainer().setUserAllowsLinkUpdate(false);
+ CPPUNIT_ASSERT(!pMedia->getSnapshot().is());
+
+#if HAVE_FEATURE_AVMEDIA
+ // Once updates are allowed the same request produces a graphic, so it was
+ // the permission alone that blocked the fetch above.
+ pShell->getEmbeddedObjectContainer().setUserAllowsLinkUpdate(true);
+ CPPUNIT_ASSERT(pMedia->getSnapshot().is());
+#endif
+}
+
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#include <svx/svdomedia.hxx>
#include <com/sun/star/text/GraphicCrop.hpp>
+#include <com/sun/star/uno/Any.hxx>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <svx/sdr/contact/viewcontactofsdrmediaobj.hxx>
#include <avmedia/mediawindow.hxx>
#include <comphelper/diagnose_ex.hxx>
+#include <comphelper/embeddedobjectcontainer.hxx>
+#include <sfx2/lnkbase.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/objsh.hxx>
using namespace ::com::sun::star;
+// A media object that points at an external URL is a document link. Registering
+// it with the link manager lets it share the normal link update permission: the
+// snapshot is only fetched from the URL once the user has allowed link updates.
+class SdrMediaLink final : public sfx2::SvBaseLink
+{
+ SdrMediaObj& mrMediaObj;
+
+public:
+ explicit SdrMediaLink(SdrMediaObj& rObj);
+
+ virtual void Closed() override;
+ virtual ::sfx2::SvBaseLink::UpdateResult DataChanged(
+ const OUString& rMimeType, const css::uno::Any& rValue) override;
+};
+
struct SdrMediaObj::Impl
{
::avmedia::MediaItem m_MediaProperties;
uno::Reference< graphic::XGraphic > m_xCachedSnapshot;
rtl::Reference<avmedia::PlayerListener> m_xPlayerListener;
OUString m_LastFailedPkgURL;
+ // owned by the link manager while registered, null otherwise
+ SdrMediaLink* m_pMediaLink = nullptr;
};
+SdrMediaLink::SdrMediaLink(SdrMediaObj& rObj)
+ : ::sfx2::SvBaseLink(::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SIMPLE_FILE)
+ , mrMediaObj(rObj)
+{
+ SetSynchron(false);
+}
+
+::sfx2::SvBaseLink::UpdateResult SdrMediaLink::DataChanged(
+ const OUString&, const css::uno::Any&)
+{
+ // Reached only after a link update was allowed, so drop the placeholder
+ // and fetch a fresh snapshot now, like a linked graphic refetches.
+ mrMediaObj.m_xImpl->m_xCachedSnapshot.clear();
+#if HAVE_FEATURE_AVMEDIA
+ mrMediaObj.m_xImpl->m_xPlayerListener.clear();
+ OUString aRealURL = mrMediaObj.m_xImpl->m_MediaProperties.getTempURL();
+ if (aRealURL.isEmpty())
+ aRealURL = mrMediaObj.m_xImpl->m_MediaProperties.getURL();
+ if (!aRealURL.isEmpty())
+ mrMediaObj.grabSnapshot(aRealURL);
+#endif
+ mrMediaObj.ActionChanged();
+ return SUCCESS;
+}
+
+void SdrMediaLink::Closed()
+{
+ mrMediaObj.m_xImpl->m_pMediaLink = nullptr;
+ SvBaseLink::Closed();
+}
+
SdrMediaObj::SdrMediaObj(SdrModel& rSdrModel)
: SdrRectObj(rSdrModel)
,m_xImpl( new Impl )
SdrMediaObj::~SdrMediaObj()
{
+ ImpDeregisterLink();
}
bool SdrMediaObj::HasTextEdit() const
OUString aRealURL = m_xImpl->m_MediaProperties.getTempURL();
if( aRealURL.isEmpty() )
+ {
+ // No extracted copy inside the document, so the media is reached
+ // through its URL. Treat it like a linked graphic and only fetch
+ // once the user has allowed link updates, so opening or converting
+ // a document never silently fetches the referenced content. A
+ // model with no document shell has no user to ask, so it does not
+ // fetch either.
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+ SfxObjectShell* pShell = pLinkManager ? pLinkManager->GetPersist() : nullptr;
+ if (!pShell || !pShell->getEmbeddedObjectContainer().getUserAllowsLinkUpdate())
+ return m_xImpl->m_xCachedSnapshot;
aRealURL = m_xImpl->m_MediaProperties.getURL();
- OUString sReferer = m_xImpl->m_MediaProperties.getReferer();
- OUString sMimeType = m_xImpl->m_MediaProperties.getMimeType();
- uno::Reference<graphic::XGraphic> xCachedSnapshot = m_xImpl->m_xCachedSnapshot;
-
- m_xImpl->m_xPlayerListener.set(new avmedia::PlayerListener(
- [this, xCachedSnapshot, aRealURL, sReferer, sMimeType](const css::uno::Reference<css::media::XPlayer>& rPlayer){
- SolarMutexGuard g;
- uno::Reference<graphic::XGraphic> xGraphic
- = m_xImpl->m_MediaProperties.getGraphic().GetXGraphic();
- m_xImpl->m_xCachedSnapshot = avmedia::MediaWindow::grabFrame(rPlayer, xGraphic);
- ActionChanged();
- }));
-
- avmedia::MediaWindow::grabFrame(aRealURL, sReferer, sMimeType, m_xImpl->m_xPlayerListener);
+ }
+ grabSnapshot(aRealURL);
}
#endif
return m_xImpl->m_xCachedSnapshot;
}
+void SdrMediaObj::grabSnapshot(const OUString& rRealURL) const
+{
+#if HAVE_FEATURE_AVMEDIA
+ OUString sReferer = m_xImpl->m_MediaProperties.getReferer();
+ OUString sMimeType = m_xImpl->m_MediaProperties.getMimeType();
+ uno::Reference<graphic::XGraphic> xCachedSnapshot = m_xImpl->m_xCachedSnapshot;
+
+ m_xImpl->m_xPlayerListener.set(new avmedia::PlayerListener(
+ [this, xCachedSnapshot, rRealURL, sReferer, sMimeType](const css::uno::Reference<css::media::XPlayer>& rPlayer){
+ SolarMutexGuard g;
+ uno::Reference<graphic::XGraphic> xGraphic
+ = m_xImpl->m_MediaProperties.getGraphic().GetXGraphic();
+ m_xImpl->m_xCachedSnapshot = avmedia::MediaWindow::grabFrame(rPlayer, xGraphic);
+ ActionChanged();
+ }));
+
+ avmedia::MediaWindow::grabFrame(rRealURL, sReferer, sMimeType, m_xImpl->m_xPlayerListener);
+#else
+ (void)rRealURL;
+#endif
+}
+
void SdrMediaObj::AdjustToMaxRect( const tools::Rectangle& rMaxRect, bool bShrinkOnly /* = false */ )
{
Size aSize( Application::GetDefaultDevice()->PixelToLogic(
{
m_xImpl->m_xCachedSnapshot.clear();
m_xImpl->m_xPlayerListener.clear();
+ // the URL is changing, so any link registered for the old one is stale
+ ImpDeregisterLink();
m_xImpl->m_MediaProperties.setFallbackURL( rNewProperties.getFallbackURL() );
OUString const& url(rNewProperties.getURL());
if (url.startsWithIgnoreAsciiCase("vnd.sun.star.Package:"))
m_xImpl->m_pTempFile.reset();
m_xImpl->m_MediaProperties.setURL(url, u""_ustr, rNewProperties.getReferer());
}
+ ImpRegisterLink();
bBroadcastChanged = true;
}
}
}
+void SdrMediaObj::ImpRegisterLink()
+{
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+ if (!pLinkManager || m_xImpl->m_pMediaLink)
+ return;
+
+#if HAVE_FEATURE_AVMEDIA
+ // Media copied into the document is extracted to a temporary file and is
+ // not a link. Only an external reference gets registered.
+ if (m_xImpl->m_pTempFile)
+ return;
+#endif
+ const OUString& rURL = m_xImpl->m_MediaProperties.getURL();
+ if (rURL.isEmpty() || rURL.startsWithIgnoreAsciiCase("vnd.sun.star.Package:"))
+ return;
+
+ // Registration happens even in a build without media playback, so the
+ // external reference stays visible to the user as a document link.
+ m_xImpl->m_pMediaLink = new SdrMediaLink(*this);
+ pLinkManager->InsertFileLink(*m_xImpl->m_pMediaLink,
+ sfx2::SvBaseLinkObjectType::ClientFile, rURL);
+}
+
+void SdrMediaObj::ImpDeregisterLink()
+{
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+ if (pLinkManager && m_xImpl->m_pMediaLink)
+ {
+ // Remove implicitly deletes the link object
+ pLinkManager->Remove(m_xImpl->m_pMediaLink);
+ m_xImpl->m_pMediaLink = nullptr;
+ }
+}
+
+void SdrMediaObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ const bool bRemove(pNewPage == nullptr && pOldPage != nullptr);
+ const bool bInsert(pNewPage != nullptr && pOldPage == nullptr);
+
+ if (bRemove)
+ ImpDeregisterLink();
+
+ SdrRectObj::handlePageChange(pOldPage, pNewPage);
+
+ if (bInsert)
+ ImpRegisterLink();
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */